package com.supermap.gwfs.message.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.supermap.gwfs.message.common.ConfigReader;
import com.supermap.gwfs.message.dao.MessageDaoService;
import com.supermap.gwfs.message.entity.ElementValue;
import com.supermap.gwfs.message.entity.Message;
import com.supermap.gwfs.message.service.AssistMessageService;
import com.supermap.gwfs.message.util.CommonTool;
import com.supermap.gwfs.message.util.FTPClientFactory;
import com.supermap.gwfs.message.util.WindCodeCalculator;
import org.apache.commons.net.ftp.FTPClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class AssistMessageServiceImpl implements AssistMessageService {

    private static final String SEVP_SPCC = "SPCC";
    private static final String SEVP_SPCC6H = "SPCC6H";
    private static final String NORMAL_MESSAGE_VALID_VALUE = "07212";
    private static final String CITY_MESSAGE_VALID_VALUE = "02406";
    private static final int NORMAL_MESSAGE_HOUR_INTERVAL = 12;
    private static final int CITY_MESSAGE_HOUR_INTERVAL = 6;
    private static Logger logger = LoggerFactory.getLogger(AssistMessageServiceImpl.class);

    @Resource
    MessageDaoService messageDaoService;

    @Override
    public String generateMessage(Map<String, String> body) {
        Properties properties = ConfigReader.getInstance().getFTPServerProperty();
        String messageCode = "NORMAL_MESSAGE";
        JSONObject result = new JSONObject();
        JSONObject param = JSONObject.parseObject(body.get("data"));
        int target = param.getInteger("target") == null ? -1 : param.getInteger("target");
        String forecastDate = param.getString("time");
        String msgDateTime = CommonTool.getUTCForecastTime(forecastDate, param.getInteger("sequence"));

        int saveCount = getMessageSaveCount(target, forecastDate, SEVP_SPCC, messageCode, "500000");
        logger.debug("Message Save Times：" + saveCount);

        //生成文件名
        String fileName = assistFileName(msgDateTime, SEVP_SPCC, NORMAL_MESSAGE_VALID_VALUE, saveCount, target, null);
        logger.debug("Assist File Name:" + fileName);

        //生成文件内容
        StringBuilder content = assistNormalMessageContent(msgDateTime, param, SEVP_SPCC, saveCount);
        logger.debug("Assist File Content:" + content);

        //文件保存到本地
        String localFilePath = this.saveMessageToLocal(msgDateTime, SEVP_SPCC, fileName, content);

        if (localFilePath == null) {
            result.put("Status", 0);
            result.put("Message", "文件保存本地失败！");
        } else {
            String serviceFTP = properties.getProperty("spccFtpServer");//服务器地址
            String loginName = properties.getProperty("spccFtpServerUser");//帐号
            String loginPassword = properties.getProperty("spccFtpServerPwd");//密码
            String uploadPath = properties.getProperty("spccFtpServerPath") + fileName;//disk3/cqpred/fplib/test/";
            boolean isUpload = uploadFileToFtpServer(serviceFTP, loginName, loginPassword, uploadPath, localFilePath);
            if (isUpload) {
                this.saveMessageToDb(target, forecastDate, SEVP_SPCC, messageCode, "500000", uploadPath, null, localFilePath);
                result.put("Status", 1);
                result.put("Message", "文件上传FTP服务器成功！");
            } else {
                result.put("Status", 0);
                result.put("Message", "文件上传FTP服务器失败！");
            }
        }

        result.put("FileName", fileName);
        result.put("FileContent", content);
        return result.toJSONString();
    }

    @Override
    public String generateCityMessage(Map<String, String> body) {
        Properties properties = ConfigReader.getInstance().getFTPServerProperty();
        String messageCode = "CITY_MESSAGE";
        JSONObject result = new JSONObject();
        JSONObject param = JSONObject.parseObject(body.get("data"));
        String forecastDate = param.getString("time");
        String msgDateTime = CommonTool.getUTCForecastTime(forecastDate, param.getInteger("sequence"));
        int target = param.getInteger("target");
        int saveCount = getMessageSaveCount(target, forecastDate, SEVP_SPCC6H, messageCode, "500000");

        //生成文件名
        String fileName = assistFileName(msgDateTime, SEVP_SPCC6H, CITY_MESSAGE_VALID_VALUE, saveCount, target, null);

        //生成文件内容
        StringBuilder content = assistCityMessageContent(msgDateTime, param, SEVP_SPCC6H, saveCount);

        //文件保存到本地
        String localFilePath = this.saveMessageToLocal(msgDateTime, SEVP_SPCC6H, fileName, content);

        if (localFilePath == null) {
            result.put("Status", 0);
            result.put("Message", "文件保存本地失败！");
        } else {
            String serviceFTPM = properties.getProperty("spcc6hFtpServerM");//服务器地址
            String loginNameM = properties.getProperty("spcc6hFtpServerMUser");//帐号
            String loginPasswordM = properties.getProperty("spcc6hFtpServerMPwd");//密码
            String uploadPathM = properties.getProperty("spcc6hFtpServerMPath") + fileName;//disk3/cqpred/fplib/test/";
            boolean isUpload = uploadFileToFtpServer(serviceFTPM, loginNameM, loginPasswordM, uploadPathM, localFilePath);
            String serviceFTPS = properties.getProperty("spcc6hFtpServerS");//服务器地址
            String loginNameS = properties.getProperty("spcc6hFtpServerSUser");//帐号
            String loginPasswordS = properties.getProperty("spcc6hFtpServerSPwd");//密码
            String uploadPathS = properties.getProperty("spcc6hFtpServerSPath") + fileName;//disk3/cqpred/fplib/test/";
            isUpload = uploadFileToFtpServer(serviceFTPS, loginNameS, loginPasswordS, uploadPathS, localFilePath) && isUpload;
            if (isUpload) {
                this.saveMessageToDb(target, forecastDate, SEVP_SPCC6H, messageCode, "500000", uploadPathM, uploadPathS, localFilePath);
                result.put("Status", 1);
                result.put("Message", "文件上传FTP服务器成功！");
            } else {
                result.put("Status", 0);
                result.put("Message", "文件上传FTP服务器失败！");
            }
        }

        result.put("FileName", fileName);
        result.put("FileContent", content);
        return result.toJSONString();
    }

    /**
     * 生成文档产品报文文件内容
     *
     * @param msgDateTime 报文时间
     * @param param       参数数据
     * @param messageType 报文类型
     * @param saveCount   保存次数
     * @return
     */
    private StringBuilder assistNormalMessageContent(String msgDateTime, JSONObject param, String messageType, int saveCount) {
        JSONObject stations = (JSONObject) param.get("data");
        JSONArray areaStation = stations.getJSONArray("areaStation");
        JSONArray autoStation = stations.getJSONArray("autoStation");

        //获取当前时间
        String timeNow = CommonTool.getUTCTime("ddHHmm", -1, 0);

        //产品名字
        String productName = "重庆市预报产品";

        //报文抬头内容
        StringBuilder header = assistMessageHeader(msgDateTime, timeNow, messageType, productName, saveCount);

        //报文站点数据内容
        StringBuilder content = new StringBuilder();

        //解析报文站点数据内容
        int stationNum = assistStationData(areaStation, content, false);
        stationNum += assistStationData(autoStation, content, false);

        header.append(stationNum).append("\r\n");

        //文件结束标志
        content.append("NNNN");
        return header.append(content);
    }

    /**
     * 生成文档产品大城市报文文件内容
     *
     * @param msgDateTime 报文时间
     * @param param       参数数据
     * @param messageType 报文类型
     * @param saveCount
     * @return
     */
    private StringBuilder assistCityMessageContent(String msgDateTime, JSONObject param, String messageType, int saveCount) {
        JSONArray station = param.getJSONArray("station");
        int target = param.getInteger("target");

        //获取当前时间
        String timeNow = CommonTool.getUTCTime("ddHHmm", target, getMinutes(target));

        //产品名字
        String productName = "重庆市6小时精细化预报产品";

        //站点数据解析报文内容
        StringBuilder content = new StringBuilder();

        //报文抬头信息
        StringBuilder header = assistMessageHeader(msgDateTime, timeNow, messageType, productName, 1);

        //固定六个天气要素编码 分别为：天气现象，最高温，最低温，风向，风速，降水量
        header.append("6").append("\r\n");
        header.append("621 622 623 624 625 626").append("\r\n");

        //解析报文站点数据内容
        int stationNum = assistStationData(station, content, true);

        header.append(stationNum).append("\r\n");

        //文件结束标志
        content.append("NNNN");
        return header.append(content);
    }

    /**
     * 站点数据解析报文内容
     *
     * @param station 站点数据：JSONArray
     * @param content 报文数据保存容器
     * @param isCity  是否大城市报文
     * @return
     */
    private int assistStationData(JSONArray station, StringBuilder content, boolean isCity) {
        int stationNum = 0;
        if (station != null) {
            stationNum = station.size();
            for (int i = 0; i < station.size(); i++) {
                content.append(this.getStationDataToString(station.getJSONObject(i), isCity));
            }
        }
        return stationNum;
    }

    /**
     * 生成报文内容抬头
     *
     * @param msgDateTime 报文时间：08or20
     * @param timeNow     当前时间
     * @param messageType 报文类型
     * @param productName 产品名
     * @param saveTimes   保存次数
     * @return
     */
    private StringBuilder assistMessageHeader(String msgDateTime, String timeNow, String messageType, String productName, int saveTimes) {
        StringBuilder content = new StringBuilder();
        String areaStr = "BECQ ";
        content.append("ZCZC").append("\r\n");
        if (saveTimes > 1) {
            char charTime = (char) (63 + saveTimes);
            String timeStr = " CC" + charTime;
            content.append("FSCI50 ").append(areaStr).append(timeNow).append(timeStr).append("\r\n");
        } else {
            content.append("FSCI50 ").append(areaStr).append(timeNow).append("\r\n");
        }
        content.append(msgDateTime).append("时").append(productName).append("\r\n");
        content.append(messageType).append(" ").append(msgDateTime).append("\r\n");
        return content;
    }

    /**
     * 生成文档产品报文文件名
     *
     * @param msgDateTime 报文时间
     * @param messageType 报文类型
     * @param validValue  时效长度
     * @param saveCount   保存次数
     * @param target      时间标记
     * @param areaCode    区域编码
     * @return 文件名
     */
    private String assistFileName(String msgDateTime, String messageType, String validValue, int saveCount, int target, String areaCode) {
        String areaStr = "BECQ";//getFileAreasStr(areaCode);
        int minutes = getMinutes(target);
        String timeNow = CommonTool.getUTCTime(target, minutes) + String.format("%02d", saveCount - 1);
        return "Z_SEVP_C_" + areaStr + "_" + timeNow + "_P_RFFC_" + messageType + "_" + msgDateTime + "00_" + validValue + ".TXT";
    }

    /**
     * 文件保存至本地目录
     *
     * @param forecastDate 预报时间
     * @param messageType  报文类型
     * @param fileName     文件名
     * @param content      文件内容
     * @return
     */
    private String saveMessageToLocal(String forecastDate, String messageType, String fileName, StringBuilder content) {
        Properties properties = ConfigReader.getInstance().getFTPServerProperty();
        String filePath = null;
        String encoding = properties.getProperty("encoding");
//        String path = this.getClass().getResource("/").getPath() + "temp/" + messageType + "/" + forecastDate;
        String path = properties.getProperty("localFilePath") + messageType + "/" + forecastDate;
        File dir = new File(path.substring(0, path.lastIndexOf("/")));
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File file;
        if (fileName != null) {
            File dir2 = new File(path);
            if (!dir2.exists()) {
                dir2.mkdirs();
            }
            file = new File(path + "/" + fileName);
        } else {
            file = new File(path);
        }

        if (file.exists()) {
            file.delete();
        }
        try {
            file.createNewFile();

            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(file), encoding));
            writer.write(content.toString());
            writer.close();
            filePath = path + "/" + fileName;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return filePath;
    }

    /**
     * FTP文件上传服务器
     *
     * @param serviceFTP     FTP服务器IP
     * @param loginName      FTP服务器用户名
     * @param loginPassword  FTP服务器密码
     * @param remoteFilePath FTP服务器目标文件路径
     * @param localFilePath  本地文件路径
     * @return
     */
    private boolean uploadFileToFtpServer(String serviceFTP, String loginName, String loginPassword, String remoteFilePath, String localFilePath) {
        FTPClient ftpClient = FTPClientFactory.loginFTPServer(serviceFTP, loginName, loginPassword);
        if (ftpClient == null) {
            return false;
        }
        boolean success = FTPClientFactory.uploadFile(ftpClient, localFilePath, remoteFilePath);
        FTPClientFactory.disconnect(ftpClient);
        return success;
    }

    /**
     * 站点数据序列化
     *
     * @param data   站点数据
     * @param isCity 是否大城市报文
     * @return
     */
    private String getStationDataToString(JSONObject data, boolean isCity) {
        WindCodeCalculator windCodeCalculator = new WindCodeCalculator();
        JSONObject station = (JSONObject) data;
        ArrayList<ElementValue> elementList = new ArrayList<ElementValue>();
        JSONArray weatherData = (JSONArray) (station.get("Weather"));
        JSONArray eda10_r = (JSONArray) (station.get("EDA10_r"));
        JSONArray eda10_z = (JSONArray) (station.get("EDA10_z"));
        JSONArray tmax = (JSONArray) (station.get("Tmax"));
        JSONArray tmin = (JSONArray) (station.get("Tmin"));
        JSONArray er06 = null;
        int hour = NORMAL_MESSAGE_HOUR_INTERVAL;
        int length = tmax.size();
        if (isCity) {
            er06 = (JSONArray) (station.get("ER06"));
            hour = CITY_MESSAGE_HOUR_INTERVAL;
            length = length / 2;
        }

        for (int i = 0; i < length; i++) {
            //Object elementData = weatherData.get(i);
            //奇数行要素值
            ElementValue singularValue = new ElementValue(hour * (2 * i + 1));
            //偶数行要素值
            ElementValue dualValue = new ElementValue(hour * (2 * i + 2));

            //新的天气现象编码需要转换为老天气现象编码;非大城市报文不处理
            singularValue.setWeather(this.getOldWeatherCode(weatherData.get(2 * i), isCity));
            dualValue.setWeather(this.getOldWeatherCode(weatherData.get(2 * i + 1), isCity));

            singularValue.setWindR(windCodeCalculator.calcDirectionCode(eda10_r.getDouble(2 * i)));
            singularValue.setWindZ(windCodeCalculator.calcSpeedCode(eda10_z.getDouble(2 * i)));

            dualValue.setWindR(windCodeCalculator.calcDirectionCode(eda10_r.getDouble(2 * i + 1)));
            dualValue.setWindZ(windCodeCalculator.calcSpeedCode(eda10_z.getDouble(2 * i + 1)));

            if (isCity) {
                singularValue.settMAX(CommonTool.getDoubleValue(tmax.get(2 * i)));
                singularValue.settMin(CommonTool.getDoubleValue(tmin.get(2 * i)));
                singularValue.setRain(CommonTool.getDoubleValue(er06.get(2 * i)));

                dualValue.settMAX(CommonTool.getDoubleValue(tmax.get(2 * i + 1)));
                dualValue.settMin(CommonTool.getDoubleValue(tmin.get(2 * i + 1)));
                dualValue.setRain(CommonTool.getDoubleValue(er06.get(2 * i + 1)));
            } else {
                dualValue.settMAX(CommonTool.getDoubleValue(tmax.get(i)));
                dualValue.settMin(CommonTool.getDoubleValue(tmin.get(i)));
            }
            elementList.add(singularValue);
            elementList.add(dualValue);
        }
        StringBuilder sb = new StringBuilder();
        DecimalFormat df = new DecimalFormat("######0.00");
        String lon = CommonTool.getStringValue((station).get("lon"));
        String lat = CommonTool.getStringValue((station).get("lat"));
        String salti = CommonTool.getStringValue((station).get("Alti"));
        sb.append(station.get("station_id")).append(" ");
        sb.append(lon).append(" ");
        sb.append(lat).append(" ");
        sb.append(salti).append(" ");
        if (!isCity) {
            sb.append(elementList.size()).append(" ");
            sb.append("21").append("\r\n");
        } else {
            sb.append(elementList.size()).append("\r\n");
        }

        for (ElementValue element : elementList) {
            if (isCity) {
                sb.append(element.toCityString());
            } else {
                sb.append(element.toString());
            }

        }
        return sb.toString();
    }

    /**
     * 新的天气现象编码需要转换为老天气现象编码;非大城市报文不处理
     *
     * @param weatherData 格点平台天气现象编码
     * @param isCity      是否为大城市报文，目前非大城市报文不处理
     * @return
     */
    private double getOldWeatherCode(Object weatherData, boolean isCity) {
        double weatherCode = CommonTool.getDoubleValue(weatherData);
        if (!isCity) {
            return weatherCode;
        }
        //格点天气现象编码 小雨：7 中雨：8 大雨：9 暴雨：10 大暴雨：11 特大暴雨：12 需要转换为老天气编码 雨：32
        if (weatherCode >= 7 && weatherCode <= 12) {
            return 32;
        }
        //格点天气现象编码 小雪：14 中雪：15 大雪：16 暴雪：17 需要转换为老天气编码 雪：33
        if (weatherCode >= 14 && weatherCode <= 17) {
            return 33;
        }
        return weatherCode;
    }

    /**
     * 数据库查询，获取当前报文保存次数
     *
     * @param hour         保存时间
     * @param forecastDate 预报时间
     * @param messageType  报文类型
     * @param messageCode  报文编码
     * @param areaCode     区域编码
     * @return
     */
    private int getMessageSaveCount(int hour, String forecastDate, String messageType, String messageCode, String areaCode) {
        Map<String, String> param = new HashMap<String, String>();
        param.put("target", String.valueOf(hour));
        param.put("forecastDate", forecastDate);
        param.put("messageType", messageType);
        param.put("messageCode", messageCode);
        param.put("areaCode", areaCode);
        Message message = messageDaoService.getMessage(param);
        if (message == null) {
            return 1;
        }
        return message.getSaveTimes() + 1;
    }

    /**
     * 保存报文信息至数据库
     *
     * @param hour           保存时间
     * @param forecastDate   预报时间
     * @param messageType    报文类型
     * @param messageCode    报文编码
     * @param areaCode       区域编码
     * @param filePathM      远程文件路径1
     * @param filePathS      远程文件路径1
     * @param originFilePath 本地文件路径
     */
    private void saveMessageToDb(int hour, String forecastDate, String messageType, String messageCode, String areaCode, String filePathM, String filePathS, String originFilePath) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Map<String, String> param = new HashMap<String, String>();
        param.put("target", String.valueOf(hour));
        param.put("forecastDate", forecastDate);
        param.put("messageType", messageType);
        param.put("messageCode", messageCode);
        param.put("areaCode", areaCode);
        Message message = messageDaoService.getMessage(param);
        if (message == null) {
            message = new Message();
            message.setAreaCode(areaCode);
            message.setForecastDate(forecastDate);
            message.setTarget(String.valueOf(hour));
            message.setSaveTimes(1);
            message.setMessageType(messageType);
            message.setMessageCode(messageCode);
            message.setFilePathM(filePathM);
            message.setFilePathS(filePathS);
            message.setOriginFilePath(originFilePath);
            message.setCreateTime(sdf.format(new Date()));
        } else {
            message.setSaveTimes(message.getSaveTimes() + 1);
            message.setFilePathM(filePathM);
            message.setFilePathS(filePathS);
            message.setOriginFilePath(originFilePath);
            message.setCreateTime(sdf.format(new Date()));
        }
        messageDaoService.saveMessage(message);
    }

    /**
     * 设置发报时间：分钟
     * 后续需要改到配置文件中去
     *
     * @param target 时间标记
     * @return
     */
    public static int getMinutes(int target) {
        if (target == 6) {
            return 45;
        }
        return 30;
    }
}
